/*---------------------------------------------------------------------
声学所ASR模型部署流程
asr大应用:acoustics_institute_asr
常规通话不受PTT控制
BIN:output/out/media_out/bin/acoustics_institute_asr
-----------------------------------------------------------------------*/
#include <iostream>
#include <cstdlib> //包含system函数
#include <unistd.h>
#include <signal.h>
#include <vector>
#include "taiic_key.h"
#include "taiic_sensor.h"
#include <thread>
#include "taiic_avsr.h"
// #include "taiic_mfcc_v3.h"
#include "../../include/rs_485/485_common.h" //485协议头文件
#include "../../include/isp/taiic_isp_model.h"
#include "../../include/taiic_uart/taiic_uart_sum.h"
#include "../../include/mfccs/taiic_mfcc_v3.h"
#include <vector>
#include <Eigen/Dense>

using namespace std;
RK_BOOL is_model_run = RK_FALSE;
long pcm_size = 0;

static void aivi_save_end(int vi, int ai); // asr

// vector<vector<uint8_t>> mfcc_feat_uint8(numFrames, vector<uint8_t>(num_mfcc_coef, 0)); // extracted mfcc feature:400*64

TAIIC_MODE_STATE_CTX_S *mode_state; // 模式状态相关参数
/*----------------------------avsr define---------------------*/
AVSR_TOOLKIT_MODEL_CTX_S *avsr_ctx; // avsr模型推理相关

RK_U8 avsr_audio_input_data[ASR_BATCH_N][ASR_FRAMES_H][ASR_MFCC_W][ASR_CHANNEL_C] = {0}; // 1*400*40*1

TEST_BATTERY_QUERY cap_query;

void *ai_uart_thread_func(void *args) // 副板->主板气导音频数据采集
{
    while (true)
    {
        std::unique_lock<std::mutex> lock(mtx_ai);
        // 等待ai_start_flag变为true
        cv_ai.wait(lock, []()
                   { return ai_start; });

        // 清除工作可用的标志，为下一次循环做准备
        ai_start = false;

        // 开始执行任务
        if (fileExists(air_pcm_path))
        {
            if (remove(air_pcm_path) == 0)
            {
                printf("air pcm file exists, and delete successfully\n");
            }
            else
            {
                printf("air pcm file delete failed\n");
            }
        }

        // 开始执行任务
        mtx_uart.lock();
        uart_main_func(5, 1, 0, 0);
        mtx_uart.unlock();

        while (1)
        {
            pcm_size = getFileSize(air_pcm_path);
            // printf("%ld\n", pcm_size);
            if (pcm_size == PCM_SIZE_SOC) // 53280 bytes
            {
                ai_end = 1; // ai finish flag
                RK_LOGD("=====save ai finish==\n");

                //end save ai -> go to mfcc,ai_data in /data/air_audio_data.pcm 16k/16bits/single channel
                mtx_uart.lock();
                uart_main_func(1, 2, 0, 0); // system("uart_commid 1 2"); // SPK嘀两声,唇图采集完成,音频采集完成
                mtx_uart.unlock();
                aivi_save_end(vi_end, ai_end);

                break;
            }
            sleep(1);
        }
    }
    pthread_exit(NULL);
}
/*---------------------------------------------------------------------
                            全局变量/宏定义/结构体/函数声明
-----------------------------------------------------------------------*/
static void sigterm_handler(int sig);
int cnt = 0;
/*----------------------------------------------------------------------------------*/
/*------------------------------------------------function-----------------------------------------------------------*/
// 电池信息查询回调
static void power_callback(int power_cap, long power_vol) // 电池信息查询回调
{
    printf("current:power is %d,voltage=%dmV\n", power_cap, power_vol / 1000);
    // 充电状态判断逻辑
    if (cap_query.count == 0)
    {
        cap_query.last_cap = power_cap;
        printf("初始电量:%d\n", power_cap);

        cap_query.init_cap = power_cap;
        cap_query.init_vol = power_vol / 1000;
    }
    else
    {
        /*------------------充电状态判断-------------------*/
        cap_query.compCap = power_cap - cap_query.last_cap;
        if (cap_query.compCap == -1)
        {
            cap_query.charge_state = 0; // 未充电
        }
        else if (cap_query.compCap == 1)
        {
            cap_query.charge_state = 1; // 充电中
        }
        else if (cap_query.compCap == 0)
        {
            cap_query.charge_state = 2; // 充满电
        }
        cap_query.last_cap = power_cap;
    }

    // 电池电量的发送模块
    if (state_send_flag) // true:send
    {
        /*---------------------有线485/蓝牙发送---------------------------*/
        if (mode_state->audio_flag == AUDIO_CONN) // 有线连接状态
        {
            bat_send_func_485(cap_query.charge_state, power_vol / 1000, power_cap);
        }
        else if (mode_state->audio_flag == AUDIO_NO_CONN) // 蓝牙模式下UART传输电量信息到副板
        {
            uart_main_func(6, cap_query.charge_state, power_vol / 1000, power_cap); // system(cap_query.bat_lable.c_str());
        }
    }

    cap_query.count = 1; // cap_query.count++;
}

static void app_key_callback(TAIIC_KEY_STATE old_state, TAIIC_KEY_NAME key_name, TAIIC_KEY_STATE key_state)
{
    // 判断key_name去实现对应功能,key_name=0表示音频线模式
    RK_LOGI("key_name, key_state,count is [%d, %d,%d]\n", key_name, key_state, cnt++);

    switch (key_name)
    {
    case TAIIC_PTT:
        mode_state->ptt_flag = key_state;
        if (mode_state->ptt_flag == PTT_DOWN)
        {
            // 特殊模式
            if ((mode_state->mode1_flag == MODE_DOWN) && (!is_model_run))
            {
                printf("PTT DOWN && lip mode\n");
                if (mode_state->audio_flag == AUDIO_NO_CONN)
                {
                    uart_main_func(3, PTT_DOWN, 0, 0); // PTT按下的键值发送上位机
                }
                // PTT按下,触发音频采集
                mtx_uart.lock();
                uart_main_func(1, 1, 0, 0); // SPK嘀一声,开始采集
                mtx_uart.unlock();

                {
                    std::lock_guard<std::mutex> lock(mtx_ai);
                    ai_start = true; // 设置标志位为true，允许ai开始执行
                }
                cv_ai.notify_one();                                // 通知等待的线程，条件已经满足
                this_thread::sleep_for(chrono::milliseconds(500)); // 0.5s
            }
        }
        break;

    case TAIIC_MODE: // 常规模式
        // 停止状态判断线程
        start_stats = false;
        cv_stats.notify_one();
        sleep(1);

        // 停止握把控制线程
        start_woba = false;
        cv_woba.notify_one();

        mode_state->mode_flag = key_state;
        if (mode_state->mode_flag == MODE_DOWN)
        {
            mtx_uart.lock();
            uart_main_func(12, 1, 0, 0); // 常规模式，通话上行数据一直发送。
            mtx_uart.unlock();

            current_mode_state = NORMAL_MODE; // 96
            if (mode_state->audio_flag == AUDIO_CONN)
            {
                mtx_uart.lock();
                uart_main_func(1, 4, 0, 0); // system("uart_commid 1 4"); // 进入有线常规模式提示音
                mtx_uart.unlock();

                mtx_uart.lock();
                uart_main_func(2, 1, 0, 0); // 切换到有线通信，启动有线下行播放
                mtx_uart.unlock();
            }
            if (mode_state->audio_flag == AUDIO_NO_CONN)
            {
                mtx_uart.lock();
                uart_main_func(1, 6, 0, 0); // system("uart_commid 1 6"); // 进入无线常规模式提示音
                mtx_uart.unlock();

                mtx_uart.lock();
                uart_main_func(2, 2, 0, 0); // 切换到蓝牙通信，启动蓝牙
                mtx_uart.unlock();
            }
        }
        break;

    case TAIIC_MODE1: // lip mode
        // 唤醒状态判断线程
        start_stats = true;
        cv_stats.notify_one();
        sleep(1);

        mode_state->mode1_flag = key_state;
        if (mode_state->mode1_flag == MODE_DOWN)
        {
            mtx_uart.lock();
            uart_main_func(12, 0, 0, 0); // 特殊模式，通话上行不发送
            mtx_uart.unlock();

            // reset vi/ai flag
            ai_end = 0;

            current_mode_state = LIP_MODE; // 97

            if (mode_state->audio_flag == AUDIO_CONN)
            {
                // 唤醒握把控制线程
                start_woba = true;
                cv_woba.notify_one();

                mtx_uart.lock();
                uart_main_func(1, 5, 0, 0); //("uart_commid 1 5");  // 进入有线特殊模式提示音
                mtx_uart.unlock();
            }
            if (mode_state->audio_flag == AUDIO_NO_CONN)
            {
                // 停止握把控制线程
                start_woba = false;
                cv_woba.notify_one();

                mtx_uart.lock();
                uart_main_func(1, 7, 0, 0); // system("uart_commid 1 7"); // 进入无线特殊模式提示音
                mtx_uart.unlock();
            }
        }
        break;

    case TAIIC_AUDIO:
        mode_state->audio_flag = key_state;
        // 有线
        if (mode_state->audio_flag == AUDIO_CONN)
        {
            mtx_uart.lock();
            uart_main_func(2, 1, 0, 0); // 切换到有线通信，启动有线下行播放
            mtx_uart.unlock();

            if (mode_state->mode1_flag == MODE_DOWN)
            {
                // 唤醒握把控制线程
                start_woba = true;
                cv_woba.notify_one();

                mtx_uart.lock();
                uart_main_func(1, 5, 0, 0); // system("uart_commid 1 5"); // 进入有线特殊模式
                mtx_uart.unlock();

                mtx_uart.lock();
                uart_main_func(12, 0, 0, 0); // 特殊模式，通话上行不发送。
                mtx_uart.unlock();

                // reset ai flag
                ai_end = 0;
            }
            else if (mode_state->mode_flag == MODE_DOWN)
            {
                // 停止握把控制线程
                start_woba = false;
                cv_woba.notify_one();

                mtx_uart.lock();
                uart_main_func(1, 4, 0, 0); // system("uart_commid 1 4"); //进入有线常规模式
                mtx_uart.unlock();

                mtx_uart.lock();
                uart_main_func(12, 1, 0, 0); // 常规模式，通话上行数据一直发送。
                mtx_uart.unlock();
            }
        }
        // 蓝牙
        if (mode_state->audio_flag == AUDIO_NO_CONN)
        {
            // 停止握把控制线程
            start_woba = false;
            cv_woba.notify_one();

            mtx_uart.lock();
            uart_main_func(2, 2, 0, 0); // 切换到蓝牙通信，启动蓝牙
            mtx_uart.unlock();

            if (mode_state->mode1_flag == MODE_DOWN)
            {
                mtx_uart.lock();
                uart_main_func(1, 7, 0, 0); // system("uart_commid 1 7"); // 进入无线特殊模式
                mtx_uart.unlock();

                mtx_uart.lock();
                uart_main_func(12, 0, 0, 0); // 特殊模式，通话上行不发送。
                mtx_uart.unlock();
                // reset vi/ai flag
                ai_end = 0;
            }
            if (mode_state->mode_flag == MODE_DOWN)
            {
                mtx_uart.lock();
                uart_main_func(1, 6, 0, 0); // system("uart_commid 1 6"); // 进入无线常规模式
                mtx_uart.unlock();

                mtx_uart.lock();
                uart_main_func(12, 1, 0, 0); // 常规模式，通话上行数据一直发送。
                mtx_uart.unlock();
            }
        }
        break;
    }
}

// youxian/bt/C/T判断
void init_mode_state_func(TAIIC_MODE_STATE_CTX_S *ctx)
{
    // initial variable
    ctx->mode_flag = -1;
    ctx->audio_flag = -1;
    ctx->ptt_flag = -1;
    ctx->mode1_flag = -1;
    // 判断初始状态为蓝牙模式/有线模式
    char *cmd_linx_mode = new char[10];
    cmd_linx_mode = concatenate("cat ", SYSFS_GPIO_AUDIO);
    ExecCmd(cmd_linx_mode, mode_rst);
    init_mode_value = std::stoi(mode_rst);
    ctx->audio_flag = init_mode_value;
    if (ctx->audio_flag == AUDIO_CONN) // 插入音频线,默认有线
    {
        printf("初始状态:有线模式\n");
    }
    else if (ctx->audio_flag == AUDIO_NO_CONN) // 未插入音频线:蓝牙
    {
        printf("初始状态:蓝牙模式\n");

        /*注释掉是为了规避气导音频上偏的问题*/
        /*mtx_uart.lock();
        uart_main_func(2, 2, 0, 0); // 切换到蓝牙通信，启动蓝牙
        mtx_uart.unlock();*/
    }
    // 判断初始状态为常规模式/特殊模式
    cmd_linx_mode = concatenate("cat ", SYSFS_GPIO_MODE);
    ExecCmd(cmd_linx_mode, mode_rst);
    init_mode_value = std::stoi(mode_rst);
    ctx->mode_flag = init_mode_value;
    if (ctx->mode_flag == MODE_DOWN) // 常规模式
    {
        printf("初始状态:常规模式\n");
        current_mode_state = NORMAL_MODE; // 96

        if (ctx->audio_flag == AUDIO_CONN)
        {
            mtx_uart.lock();
            uart_main_func(1, 4, 0, 0); // 进入有线常规模式提示音
            mtx_uart.unlock();
        }
        else if (ctx->audio_flag == AUDIO_NO_CONN)
        {
            mtx_uart.lock();
            uart_main_func(1, 6, 0, 0); // 进入无线常规模式提示音
            mtx_uart.unlock();
        }
    }
    else if (ctx->mode_flag == MODE_UP) // 释放状态
    {
        cmd_linx_mode = concatenate("cat ", SYSFS_GPIO_MODE1);
        ExecCmd(cmd_linx_mode, mode_rst);
        init_mode_value = std::stoi(mode_rst);
        ctx->mode1_flag = init_mode_value;
        if (ctx->mode1_flag == MODE_DOWN) // 特殊模式
        {
            printf("初始状态:特殊模式\n");

            mtx_uart.lock();
            uart_main_func(12, 0, 0, 0); // 特殊模式，通话上行不发送
            mtx_uart.unlock();

            // 首次为特殊，启动状态线程的判断
            start_stats = true;
            cv_stats.notify_one();
            sleep(1);

            current_mode_state = LIP_MODE; // 97

            if (mode_state->audio_flag == AUDIO_CONN) // 有线特殊
            {
                mtx_uart.lock();
                uart_main_func(1, 5, 0, 0); // 进入有线特殊模式提示音
                mtx_uart.unlock();

                // 开启握把控制线程
                start_woba = true;
                cv_woba.notify_one(); // 通知等待的线程，条件已经满足
            }
            else if (ctx->audio_flag == AUDIO_NO_CONN)
            {
                mtx_uart.lock();
                uart_main_func(1, 7, 0, 0); // 进入无线特殊模式提示音
                mtx_uart.unlock();
            }
        }
        else if (ctx->mode1_flag == MODE_UP)
        {
            printf("特殊模式键 释放状态\n");
        }
    }

    mtx_uart.lock();
    uart_main_func(1, 3, 0, 0); // system("uart_commid 1 3"); //设备初始化完成提示音
    mtx_uart.unlock();
}

// 模式状态线程函数，每隔3秒发送一次
void *mode_state_upload_thread_func(void *arg)
{
    while (1)
    {
        std::unique_lock<std::mutex> lock(mtx_state);
        // 等待state_send_flag变为true
        cv_state.wait(lock, []()
                      { return state_send_flag; });

        if (mode_state->audio_flag == AUDIO_NO_CONN) // 蓝牙连接
        {
            mtx_uart.lock();
            uart_main_func(8, current_mode_state, 0, 0); // Bluetooth:SPP
            mtx_uart.unlock();
        }
        else if (mode_state->audio_flag == AUDIO_CONN) // 有线连接
        {
            // rs_send((char *)current_mode_state, 2); // 485 send
            mode_state_send_func_485(current_mode_state);
        }
        sleep(3); // 暂停3秒
    }
    pthread_exit(NULL);
}

static void aivi_save_end(int vi, int ai) // asr
{
    /*mtx_uart.lock();
    uart_main_func(1, 8, 0, 0); //模型推理开始提示音
    mtx_uart.unlock();*/

    // load audio pcm as input
    audioData = loadAudioData(air_pcm_path);
    // mfcc feature extraction, output type:uint8
    extract_mfcc(audioData, mfcc_feat_u8);
    //将matrix中的数据赋值到avsr_audio_input_data
    for (int i = 0; i < ASR_FRAMES_H; i++)
    {
        for (int j = 0; j < ASR_MFCC_W; j++)
        {
            avsr_audio_input_data[0][i][j][0] = static_cast<RK_U8>(mfcc_feat_u8(i, j));
        }
    }
    //送入模型
    unsigned char *avsr_input_data[avsr_ctx->io_num.n_input];

    avsr_input_data[0] = new unsigned char[avsr_ctx->input_attrs[0].size]; // 设置audio 输入大小
    avsr_input_data[0] = &avsr_audio_input_data[0][0][0][0];

    RK_LOGI("===MFCC END==\n");
    avsr_rknn_toolkit_data_refresh(avsr_ctx, avsr_input_data);
    int ret = rknn_run(avsr_ctx->context, NULL);
    MODEL_RESULT_S avsr_result = avsr_rknn_toolkit_result_int8(avsr_ctx);
    is_model_run = RK_FALSE;

    printf("===result label is %d, prob is %f====\n", avsr_result.label, avsr_result.prob);

   /*mtx_uart.lock();
    uart_main_func(1, 9, 0, 0); //模型推理完成提示音
    mtx_uart.unlock();*/

    // 485 send
    if (mode_state->audio_flag == AUDIO_CONN)
    {
        lip_label_send_func_485(avsr_result.label);
    }
    else if (mode_state->audio_flag == AUDIO_NO_CONN) // 蓝牙模式lip label通过UART口发送到副板
    {
        mtx_uart.lock();
        uart_main_func(9, avsr_result.label, 0, 0);
        mtx_uart.unlock();
    }

    is_model_run = RK_FALSE;
    // reset ai flag
    ai_end = 0;

    // 开始状态线程的判断
    state_send_flag = true;
    cv_state.notify_one();

    /*mtx_uart.lock();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    uart_main_func(1, 11, 0, 0); // 开始下一指令提示音
    mtx_uart.unlock();*/
}

// 485youxian recv function,WOBA PTT control lip capture
void *rs_recv(void *arg)
{
    uint8_t recv_buf[BUF_RECV_SIZE] = {0}; // 第一个元素被初始化为0，其余元素默认为0
    int rd = 0;
    int fs_sel;
    fd_set fs_read;
    struct timeval respTime;
    int fd = open(UART_DEV_NAME, O_RDWR | O_NOCTTY | O_NDELAY); // 打开串口
    UART0_INIT(fd);

    while (1)
    {
        std::unique_lock<std::mutex> lock(mtx_woba);
        // 等待start_woba变为true
        cv_woba.wait(lock, []()
                     { return start_woba; });
        printf("woba:start 485:youxian+lip\n");
        // receive buffer
        FD_ZERO(&fs_read);
        FD_SET(fd, &fs_read);
        respTime.tv_sec = 10;
        respTime.tv_usec = 0;

        fs_sel = select(FD_SETSIZE, &fs_read, NULL, NULL, &respTime);
        if (fs_sel == 0)
        {
            continue;
        }
        memset(recv_buf, 4, sizeof(recv_buf)); // recv_buf的所有元素都设置为4
        rd = read(fd, recv_buf, BUF_RECV_SIZE);

        printf("uint8_t recv_buffer:%d\n", recv_buf[0]);

        // WOBA PTT control lip capture
        if (recv_buf[0] == WOBA_KEY_DOWN) // 0: woba ptt down
        {
            if ((!is_model_run) && (mode_state->mode1_flag == MODE_DOWN))
            {
                printf("PTT DOWN && lip mode\n");

                // SPP收到键值0，触发音频采集
                {
                    std::lock_guard<std::mutex> lock(mtx_ai);
                    ai_start = true; // 设置标志位为true，允许ai开始执行
                }
                cv_ai.notify_one();
            }
        }
        else if (recv_buf[0] == WOBA_KEY_UP) // 1: woba ptt up
        {
            printf("woba PTT UP && lip mode\n");
        }
        else if (recv_buf[0] == CONNECT_IIIS) // 2:CONNECT IIIS
        {
            printf("connect iiis!\n");
            diantai_flag = false;
        }
        else if (recv_buf[0] == BATTERY_START_SEND) // 3:表示开始发送初始电量
        {
            bat_send_func_485(cap_query.charge_state, cap_query.init_vol, cap_query.init_cap);

            // 有线485发送固件版本号
            uart_main_func(14, 1, 0, 0);
        }
    }
    close(fd);
    pthread_exit(NULL);
}
/*----------------------------------------------------------------
                            主函数main
-----------------------------------------------------------------*/
int main(int argc, char **argv)
{
    // UART串口初始化
    serial_init();

    // initial mode state
    mode_state = reinterpret_cast<TAIIC_MODE_STATE_CTX_S *>(malloc(sizeof(TAIIC_MODE_STATE_CTX_S)));
    memset(mode_state, 0, sizeof(TAIIC_MODE_STATE_CTX_S));

    // initial avsr model
    avsr_ctx = reinterpret_cast<AVSR_TOOLKIT_MODEL_CTX_S *>(malloc(sizeof(AVSR_TOOLKIT_MODEL_CTX_S))); // 分配内存空间
    memset(avsr_ctx, 0, sizeof(AVSR_TOOLKIT_MODEL_CTX_S));

    // config avsr model
    // avsr_ctx->modelPath = ACOUSTICS_ASR_MODEL_PATH;
    avsr_ctx->modelPath = argv[1];
    avsr_rknn_toolkit_config_init(avsr_ctx);
    avsr_rknn_toolkit_io_init(avsr_ctx);
    RK_LOGD("====taiic init model config successfully===\n");

    // Mode Initialization Judgment
    init_mode_state_func(mode_state);

    // 按键检测
    taiic_all_key_registers(mode_state, app_key_callback);
    // 电量查询回调
    taiic_batt_power_registers(power_callback);

    // 模式状态每隔3s上传一次状态值
    pthread_t tid_mode_state_upload;
    pthread_create(&tid_mode_state_upload, NULL, mode_state_upload_thread_func, NULL);
    // /*pthread_join(tid_mode_state_upload,NULL);*/
    /*--------------------------start isp thread end--------------------------------*/
    // ai _save end_callback
    aivi_save_end_callback(aivi_save_end);
    // audio_capture
    pthread_t tid_ai_uart;
    pthread_create(&tid_ai_uart, NULL, ai_uart_thread_func, NULL);

    // spp receive data from host
    uart_main_func(10, 0, 0, 0);
    pthread_join(tid_ai_uart, NULL);

    // setup the SIGINT to ctrl+c and handing de-init process
    signal(SIGINT, sigterm_handler);

__FAILED_VSR:
    if (avsr_ctx)
    {
        free(avsr_ctx);
        avsr_ctx = RK_NULL;
    }

__FAILED_SYS:
    RK_MPI_SYS_Exit();
    if (mode_state)
    {
        free(mode_state);
        mode_state = RK_NULL;
    }

    // 释放循环缓冲区
    serialData.releaseRingbuffer();

    return 0;
}
/*---------------------------------------------------------------
                          函数实现
-----------------------------------------------------------------*/
static void sigterm_handler(int sig)
{
    RK_LOGI("Catched SIGINT %d\n", sig);
}
